package com.intellekt.ecert.ws.client;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.SocketTimeoutException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.ws.client.core.support.WebServiceGatewaySupport;
import org.springframework.ws.soap.client.core.SoapActionCallback;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.intellekt.ecert.sps.GetPCITCertificateByInfo;
import com.intellekt.ecert.sps.ObjectFactory;
import com.intellekt.ecert.sps.SPSCertificateType;

public class Client extends WebServiceGatewaySupport {

	private static final Logger log = Logger.getLogger(Client.class);

	private static final String INFO_ENDPOINT = "https://pcit-training.aphis.usda.gov/ECertServiceImpl/ECertService/GetCertificateByInfo";
	private static final String ID_ENDPOINT = "https://pcit-training.aphis.usda.gov/ECertServiceImpl/ECertService/GetCertificateById";

	@SuppressWarnings({ "unchecked" })
	public List<SPSCertificateType> getCertificatesByInfo(String from, String to) throws Exception {
		if (log.isDebugEnabled()) {
			log.debug("getCertificates...");
			log.debug("from ->  " + from);
			log.debug("to ->  " + to);
		}

		GetPCITCertificateByInfo request = new GetPCITCertificateByInfo();
		request.setIssueCountry("MX");
		request.setDateIssueFrom(from);
		request.setDateIssueTo(to);
		request.setIssueLocation("?");

		JAXBElement<String> response = (JAXBElement<String>) getWebServiceTemplate().marshalSendAndReceive(request, new SoapActionCallback(INFO_ENDPOINT));

		if(StringUtils.isBlank(response.getValue())){
			return null;
		}
		
		log.debug(response.getValue().toString());
		byte[] decoded = Base64.decodeBase64(response.getValue());

		return unmarshalResponse(decoded);
	}

	@SuppressWarnings({ "unchecked" })
	public SPSCertificateType getCertificate(String id) throws Exception {
		if (log.isDebugEnabled()) {
			log.debug("getCertificate...");
			log.debug("getCertificate ->  " + id);
		}
		ObjectFactory of = new ObjectFactory();
		JAXBElement<String> request = of.createGetPCITCertificateByID(id);

		JAXBElement<String> response = (JAXBElement<String>) getWebServiceTemplate().marshalSendAndReceive(request, new SoapActionCallback(ID_ENDPOINT));

		if(StringUtils.isBlank(response.getValue())){
			return null;
		}
		byte[] decoded = Base64.decodeBase64(response.getValue());
		// byte[] decoded = null;// = Base64.decodeBase64(response.getValue());
		List<SPSCertificateType> result = unmarshalResponsecert(decoded);
		log.debug("size ->  " + result.size());
		if (result.size() > 0) {
			return result.get(0);
		} else {
			return null;
		}
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public List<SPSCertificateType> unmarshalResponse(byte[] decoded) throws Exception {

		ArrayList<SPSCertificateType> certificados = new ArrayList();

		// try {
		String certs = new String(decoded, "UTF-8");
		// certs = certs.replace("</certificates>", "");
		JAXBContext jaxbContext = JAXBContext.newInstance(com.intellekt.ecert.sps.SPSCertificateType.class);
		Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();

		// TODO borrar esto que es de prueba
		// String respuesta =
		// getStringFromInputStream(response.getInputStream());
		// InputStream xml = new
		// ByteArrayInputStream(respuesta.getBytes(StandardCharsets.UTF_8));

		// log.debug("XML RECEIVED" + respuesta);
		InputStream xml = new ByteArrayInputStream(certs.getBytes(StandardCharsets.UTF_8));

		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		dbf.setNamespaceAware(true);
		DocumentBuilder db = dbf.newDocumentBuilder();
		Document doc = db.parse(xml);

		NodeList nl = doc.getFirstChild().getChildNodes();

		if (nl != null && nl.getLength() > 0) {
			log.debug("Parsing Response");
			for (int i = 0; i < nl.getLength(); i++) {
				if (nl.item(i).getNodeType() == Node.ELEMENT_NODE) {
					Element el = (Element) nl.item(i);
					JAXBElement<SPSCertificateType> cert = (JAXBElement<SPSCertificateType>) unmarshaller.unmarshal(el, SPSCertificateType.class);

					certificados.add(cert.getValue());

				}
			}
		}

		return certificados;
		/*
		 * } catch (UnsupportedEncodingException e) { e.printStackTrace();
		 * return certificados; } catch (JAXBException e) { e.printStackTrace();
		 * return certificados; } catch (ParserConfigurationException e) {
		 * e.printStackTrace(); return certificados; } catch (SAXException e) {
		 * throw new SAXException(
		 * "Se recibio un XML erroneo, por favor informe sobre este error"); }
		 * catch (IOException e) { e.printStackTrace(); return certificados; }
		 * catch (NullPointerException e) {
		 * log.error("No se encontraron certificados"); return certificados; }
		 * catch (IndexOutOfBoundsException e) {
		 * log.error("No se encontraron certificados"); e.printStackTrace();
		 * return certificados; }
		 */

	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public List<SPSCertificateType> unmarshalResponsecert(byte[] decoded) throws Exception {

		ArrayList<SPSCertificateType> certificados = new ArrayList();

		// try {
		String certs = new String(decoded, "UTF-8");

		JAXBContext jaxbContext = JAXBContext.newInstance(com.intellekt.ecert.sps.SPSCertificateType.class);
		Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();

		InputStream xml = new ByteArrayInputStream(certs.getBytes(StandardCharsets.UTF_8));

		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		dbf.setNamespaceAware(true);
		DocumentBuilder db = dbf.newDocumentBuilder();
		Document doc = db.parse(xml);

		NodeList nl = doc.getChildNodes();

		if (nl != null && nl.getLength() > 0) {
			log.debug("Parsing Response");
			for (int i = 0; i < nl.getLength(); i++) {
				if (nl.item(i).getNodeType() == Node.ELEMENT_NODE) {
					Element el = (Element) nl.item(i);
					JAXBElement<SPSCertificateType> cert = (JAXBElement<SPSCertificateType>) unmarshaller.unmarshal(el, SPSCertificateType.class);

					certificados.add(cert.getValue());

				}
			}
		}

		return certificados;
		/*
		 * } catch (UnsupportedEncodingException e) { e.printStackTrace();
		 * return certificados; } catch (JAXBException e) { e.printStackTrace();
		 * return certificados; } catch (ParserConfigurationException e) {
		 * e.printStackTrace(); return certificados; } catch (SAXException e) {
		 * throw new SAXException(
		 * "Se recibio un XML erroneo, por favor informe sobre este error"); }
		 * catch (IOException e) { e.printStackTrace(); return certificados; }
		 * catch (NullPointerException e) {
		 * log.error("No se encontraron certificados"); return certificados; }
		 * catch (IndexOutOfBoundsException e) {
		 * log.error("No se encontraron certificados"); e.printStackTrace();
		 * return certificados; }
		 */

	}

	public String getStringFromInputStream(InputStream is) throws IOException {
		BufferedInputStream bis = new BufferedInputStream(is);
		ByteArrayOutputStream buf = new ByteArrayOutputStream();
		int result = bis.read();
		while (result != -1) {
			byte b = (byte) result;
			buf.write(b);
			result = bis.read();
		}
		return buf.toString();
	}

}
